// 《围棋》作者版权所有。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package poll

import (
	"internal/itoa"
	"runtime"
	"sync"
	"syscall"
)

// 异步IO实现异步可取消I/O。
// 异步IO表示单个异步读写
// 操作。结果将在结果通道上返回。
// 正在进行的I/O系统调用可以完成，也可以被注释打断。
type asyncIO struct {
	res chan result

	// mu守卫pid字段。
	mu sync.Mutex

	// pid保存运行IO操作的进程的进程id。
	pid int
}

// 结果是读或写操作的返回值。
type result struct {
	n   int
	err error
}

// newAsyncIO通过调用fn返回一个新的异步IO，该异步IO执行I/O 
// 操作，fn必须执行一个且仅执行一个
// 可中断系统调用。
func newAsyncIO(fn func([]byte) (int, error), b []byte) *asyncIO {
	aio := &asyncIO{
		res: make(chan result, 0),
	}
	aio.mu.Lock()
	go func() {
		// 将当前goroutine锁定到其进程
		// 并将pid存储在io中，以便Cancel可以中断它。我们忽略了“挂断”信号
		// 因此该信号不会关闭整个
		// Go运行时。
		runtime.LockOSThread()
		runtime_ignoreHangup()
		aio.pid = syscall.Getpid()
		aio.mu.Unlock()

		n, err := fn(b)

		aio.mu.Lock()
		aio.pid = -1
		runtime_unignoreHangup()
		aio.mu.Unlock()

		aio.res <- result{n, err}
	}()
	return aio
}

// 取消中断I/O操作，导致
// 等待函数返回。
func (aio *asyncIO) Cancel() {
	aio.mu.Lock()
	defer aio.mu.Unlock()
	if aio.pid == -1 {
		return
	}
	f, e := syscall.Open("/proc/"+itoa.Itoa(aio.pid)+"/note", syscall.O_WRONLY)
	if e != nil {
		return
	}
	syscall.Write(f, []byte("hangup"))
	syscall.Close(f)
}

// 等待I/O操作完成。
func (aio *asyncIO) Wait() (int, error) {
	res := <-aio.res
	return res.n, res.err
}

// 运行时提供的以下函数用于忽略和取消忽略进程接收到的“挂断”信号。
func runtime_ignoreHangup()
func runtime_unignoreHangup()
